home *** CD-ROM | disk | FTP | other *** search
/ Aminet 7 / Aminet 7 - August 1995.iso / Aminet / misc / sci / RARS_Amiga_3.lha / RARS / carz.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  1995-05-27  |  45.3 KB  |  1,128 lines

  1. // CARZ.CPP - simulated robot car racing - begin development Dec. '94
  2. // by Mitchell E. Timin, State College, PA
  3. // see CAR.H & TRACK.H for class and structure declarations
  4. // This version is for Borland C++, version 3.1, and is for DOS
  5. // This is part of version 0.60 of RARS (Robot Auto Racing Simulation)
  6. // ver. 0.1 release January 12, 1995
  7. // ver. 0.2 1/23/95
  8. // ver. 0.3 2/7/95
  9. // ver. 0.39 3/6/95
  10. // ver. 0.45 3/21/95
  11. // ver. 0.50 4/5/95
  12. // ver. 0.6b 5/8/95 b for beta
  13.  
  14. #include <math.h>
  15. #include <ctype.h>
  16. #include <stdlib.h>
  17. #include <string.h>
  18. #include <iostream.h>
  19. #include "car.h"
  20. #include "track.h"
  21. #include "os.h"
  22. #include "misc.h"
  23.  
  24. static const double PM = 1e5;    // Power, Maximum, 100,000 ft. lb. per sec
  25. static const double DRAG_CON = .0065;  // air drag, lb. per (ft/sec)^2
  26. static const double M = 75;  // mass, slugs   (about 2400 lb.)
  27. static const double SFC = .45e-6;  // Specific Fuel Consumption, lb. per ft. lb.
  28. static const double FUEL_START = 150;           // inital fuel supply, lb.
  29. static const double REFILL_SECONDS = 12.0;      // time to refill the tank
  30. static const unsigned long MAX_DAMAGE = 30000;  // out of race with this damage
  31. static const double STARTING_SPEED = 40.0;  // cars start at this speed, ft/sec
  32. static const double REVERSE_GEAR_LIMIT = 20; // ft/sec max to allow reverse
  33. static const int PRIV_DATA_SIZE = 4096;  // size of robot's private data area
  34.  
  35. // file scope global variables:
  36. static char *glob_name;  // will hold name of each robot driver (one at a time)
  37. static double time_count;  // elapsed time, seconds
  38. static segment *trackout; // global variables that describe the track:
  39. static segment *trackin;  // trackin and trackout are arrays of segments
  40. static  int  NSEG;        // the number of segments of the track
  41. static  double  width;    // track width, feet
  42. static STAGE stage = BEFORE;  // see CAR.H
  43. static int done_count;  // incremented by each car that finishes the race
  44. static int out_count;   // incremented by each car that crashes
  45. static int* order;                 // will point to array of positions
  46. static int* position;              // re-arrangement of order array
  47. static int* new_data;              // points to array of new data indicators
  48. static colors car_colors[MAXCARS]; // There should be MAXCARS of these color pairs:
  49.  
  50. // global variables:
  51. Car* pcar[MAXCARS];                // array of pointers to the various cars
  52. int lap[MAXCARS];                  // set, then cleared as each lap completes
  53. double length;       // total lenth of track (average of inner and outer rails)
  54. // These are set by the command line arguments: (see get_args() in OS.CPP)
  55. int lap_count;       // length of the race in laps
  56. int car_count;       // how many cars in the race
  57. int race_count;      // how many races?
  58. int real_speed;      // if non-zero, PC clock will control speed of race
  59. int no_display;      // if non-zero, race will be invisible
  60. int keep_order;      // if this is 0, then starting order will be re-arranged
  61. int surface;         // surface type, 0 is looser, 1 is harder. (see friction())
  62. static int designated = -1; // index of a keyboard-selected car; -1 means no selection
  63.  
  64. // These are the control or "driver" programs which compete in the race:
  65. con_vec cntrl0(situation &s);  // See drivers[] below, and CAR.H
  66. con_vec cntrl5(situation &s);
  67. con_vec cntrlR(situation &s);
  68. con_vec dynamic(situation &s);
  69. con_vec Arelys(situation &s);
  70. con_vec TutMan3(situation &s);
  71. con_vec Valerie(situation &s);
  72. con_vec Bingo1(situation &s);
  73. con_vec Ramdu(situation &s);
  74. con_vec Rusty(situation &s);
  75. con_vec Heath2(situation &s);
  76. con_vec Blender(situation &s);
  77. con_vec Burns(situation &s);
  78. con_vec Indretti(situation &s);
  79. con_vec OscCar2(situation &s);
  80. con_vec WappuCar(situation &s);
  81.  
  82. // This is the permanent array of available drivers.
  83. // Their order here determines their car colors.
  84. car_ID drivers[] = {
  85.    { cntrlR,  {car_clrs[0].nose, car_clrs[0].tail},  (char *)0 },
  86.    { Burns,  {car_clrs[1].nose, car_clrs[1].tail},  (char *)0 },
  87.    { Bingo1,  {car_clrs[2].nose, car_clrs[2].tail},  (char *)0 },
  88.    { Blender, {car_clrs[3].nose, car_clrs[3].tail},  (char *)0 },
  89.    { Indretti, {car_clrs[4].nose, car_clrs[4].tail},  (char *)0 },
  90.    { Arelys,  {car_clrs[5].nose, car_clrs[5].tail},  (char *)0 },
  91.    { Heath2,  {car_clrs[6].nose, car_clrs[6].tail},  (char *)0 },
  92.    { Ramdu,  {car_clrs[7].nose, car_clrs[7].tail},  (char *)0 },
  93.    { cntrl0,  {car_clrs[8].nose, car_clrs[8].tail},  (char *)0 },
  94.    { OscCar2,  {car_clrs[9].nose, car_clrs[9].tail},  (char *)0 },
  95.    { cntrl5,  {car_clrs[10].nose, car_clrs[10].tail}, (char *)0 },
  96.    { Rusty,    {car_clrs[11].nose, car_clrs[11].tail}, (char *)0 },
  97.    { TutMan3, {car_clrs[12].nose, car_clrs[12].tail}, (char *)0 },
  98.    { Valerie, {car_clrs[13].nose, car_clrs[13].tail}, (char *)0 },
  99.    { dynamic, {car_clrs[14].nose, car_clrs[14].tail}, (char *)0 },
  100.    { WappuCar, {car_clrs[15].nose, car_clrs[15].tail}, (char *)0 }  };
  101.  
  102. void report_overall(int, int, car_ID*, long);          // in REPORT.CPP
  103. void report_results(int, int*, car_ID*, Car**, int*);  // in REPORT.CPP
  104. void RAM_report(void);                                 // in REPORT.CPP
  105.  
  106. // these things are defined in DRAW.CPP:
  107. void instruments(int);            // updates the instrument panel
  108. void designate(int i);            // marks car i on scoreboard
  109. extern void drawcar(double, double, double, int, int);
  110. extern void lapper(int which, int lap);  // shows lap count on scoreboard,
  111. extern void resume_normal_display(void);
  112. extern void leaders(int, int*);
  113. extern void scoreboard(STAGE);
  114. extern void graph_setup(void);
  115. extern void refresh_finish_line(void);
  116. extern double drawpath(double, double, double, segment*);
  117.  
  118. //----------------------------------------------------------
  119. // Random Variable Generation:
  120. #define MULTIPLIER      0x015a4e35L
  121. #define INCREMENT       1
  122. const int MAXRAND = 0x7FFF;
  123. static  long   Seed = 1;
  124. static  long   Seed2 = 1;
  125. static  long   Seed_to_report = 1;
  126.  
  127. inline int raand(void)   // This one is for use within this file.
  128. {
  129.         Seed = MULTIPLIER * Seed + INCREMENT;
  130.         return((int)(Seed >> 16) & 0x7FFF);
  131. }
  132.  
  133. // Command line processing in OS.CPP can either call this with input of 0,
  134. // or a user's entry, or not call it.  0 input means really randomize.
  135. // If not called at all, the default seed of 1 is used.
  136. void Randomize(long input)
  137. {
  138.    if(input)              // This is when the user gives the seed
  139.       Seed = input;
  140.    else                   // this is when the user wants to randomize
  141.       Seed = pick_random();    // get a random no. from the op. sys.
  142.    Seed_to_report = Seed;   // this won't change again.
  143.    Seed2 = (long)raand();
  144. }
  145.  
  146. int rand(void)    // robots and other functions can use this one:
  147. {
  148.         Seed2 = MULTIPLIER * Seed2 + INCREMENT;
  149.         return((int)(Seed2 >> 16) & 0x7FFF);
  150. }
  151. //---------------------------------------------------------------------
  152.  
  153. void my_name_is(const char* name)  // for a robot driver to identify itself
  154. {
  155.       strcpy(glob_name, name);
  156. }
  157.  
  158. inline double vec_mag(double x, double y)  // sqrt of sum of squares
  159. {                                          // (used for vector magnitude)
  160.    return sqrt(x * x + y * y);
  161. }
  162.  
  163. inline int incseg(int seg)       // returns the next segment of the track
  164. {                                // (i.e. 0 yields 1, 1 yields 2, but
  165.    if(++seg == NSEG)             // NSEG-1 yields 0)
  166.       seg = 0;
  167.    return seg;
  168. }
  169.  
  170. // This is the model of the track friction force on the tire.  This force
  171. // provides propulsion, cornering, and braking.  Force is assumed to depend
  172. // only on the slip speed, rising very rapidly with small slip speed,
  173. // and then asymtotically approaching an upper limit.  (This is similar to
  174. // tires on unpaved surfaces.)  There are two models here, the global
  175. // variable "surface" chooses between them.
  176. const double MYU_MAX0 = 1.0;  // maximum coeficient of friction, tire vs. track:
  177. const double MYU_MAX1 = 1.05; // maximum coeficient of friction, tire vs. track:
  178. const double SLIPPING = 2.0;
  179. // SLIPPING is the slip speed at which:
  180. //    for type 0, half maximum force is reached.
  181. //    for type 1, .632 of maximum force is reached.
  182. inline double friction(double slip)     // returns the coef. of friction,
  183. {                                       // given the slip speed, ft. per sec.
  184.    if(surface)
  185.       return MYU_MAX1 * (1.0 - exp(-slip/SLIPPING));   // (harder surface)
  186.    else
  187.       return (MYU_MAX0 * slip)/(SLIPPING + slip);      // (very loose surface)
  188. }
  189.  
  190. // A version of the type 0 model with randomness in the "slipping" variable.
  191. // The effect is small at high slip rates, large at small slip rates.
  192. inline double rfriction(double slip)     // returns the coef. of friction,
  193. {                                       // given the slip speed, ft. per sec.
  194.    double slipping;     // slip speed at which half maximum force is reached
  195.  
  196.    slipping = 1.5 + (double)raand()/MAXRAND;    // range is 1.5 to 2.5 fps
  197.    if(surface)
  198.       return MYU_MAX1 * (1.0 - exp(-slip/slipping));   // (harder surface)
  199.    else
  200.       return (MYU_MAX0 * slip)/(slipping + slip);      // (very loose surface)
  201. }
  202.  
  203. // Comparison routine for determining who's ahead: (used when sorting)
  204. // Returns 0 iff car0 has gone farther than car1, 1 otherwise.
  205. int farther(Car* car0, Car* car1)
  206. {
  207.    int blok0, blok1;  /* A block is the same as a track segment except for
  208.                          segment 0.  For segment 0, the block is 0 when the
  209.                          car is past the finish line.  When the car is heading
  210.                          toward the finish line the block is NSEG.  */
  211.    blok0 = car0->seg_id;
  212.    if(!blok0)
  213.       if(car0->to_end > from_start_to_seg1)
  214.          blok0 = NSEG;
  215.    blok1 = car1->seg_id;
  216.    if(!blok1)
  217.       if(car1->to_end > from_start_to_seg1)
  218.          blok1 = NSEG;
  219.    if(car0->laps > car1->laps)
  220.       return 0;
  221.    else if(car0->laps < car1->laps)
  222.       return 1;                              // else laps are equal
  223.    else if(blok0 > blok1)
  224.       return 0;
  225.    else if(blok1 > blok0)
  226.       return 1;                            // else they are in the same blok:
  227.    else if(car0->to_end <= car1->to_end)
  228.       return 0;
  229.    else return 1;        // returns 1 in case of total equality
  230. }
  231.  
  232. // a sorting routine that's fast when things are already in order:
  233. // This routine updates the order[] array which has the position of each
  234. // car.  order[0] is the ID of the leader, order[1] is in second place, etc.
  235. // Also, the new_data[] array is set to show which positions have changed.
  236. void sortem(int num_disp)
  237. {
  238.    int i, temp;
  239.    int old_order[MAXCARS];
  240.  
  241.    for(i=0; i<num_disp; i++)     // copy first part of order[] array
  242.       old_order[i] = order[i];
  243.  
  244.    for(i=0; i<car_count-1; i++) // the while loop below does not usually repeat:
  245.       while(farther(pcar[order[i]], pcar[order[i+1]]))  {
  246.          temp = order[i];     // When a car passes another, we swap positions
  247.          order[i] = order[i+1];
  248.          order[i+1] = temp;
  249.          if(i > 0)              // now we have to see if it passed another car
  250.             --i;
  251.       }
  252.  
  253.    for(i=0; i<num_disp; i++)     // see what has changed and flag it:
  254.       new_data[i] = (old_order[i] != order[i]);
  255. }
  256.  
  257. // returns laps and passes seg_id and distance to next segment via pointers:
  258. inline int Car::where_is_it(int* seg_id_addr, double* to_end_addr)
  259. {
  260.    *seg_id_addr = seg_id;
  261.    *to_end_addr = to_end;
  262.    return laps;
  263. }
  264.  
  265. // the purpose of control() is to call the car's driver function, and
  266. // then to return the steering and throttle settings produced by
  267. // that routine.
  268. void Car::control(situation& s)  // calls the control function via the pointer
  269. {                               // that was installed in the car object by
  270.    con_vec output;              // by the constructor.
  271.  
  272.    if(out)
  273.       output.alpha = output.vc = 0.0;  // no action if out of race
  274.    else {
  275.       s.data_ptr = data_ptr;  // the robot's data area in RAM
  276.       s.starting = starting;  // startup signal for the robot
  277.       starting = 0;
  278.       output = (*cntrl)(s);   // call the robot driver to set alpha and vc
  279.    }
  280.    alpha = output.alpha;   vc = output.vc;  // set private vars in car object
  281. }
  282.  
  283. // This function is used only by zbrent().  It calculates power delivered
  284. // to the tires using the same formulas as in move_car().  The value
  285. // returned to the caller is that power minus 99.75% of maximum power.
  286. // The target power used by zbrent() is therefore .9975 * PM.
  287. // We use this instead of full power because zbrent() can err slightly
  288. // on either side of the target, and we should not exceed PM.
  289. double power_excess(double vc, double sine, double cosine, double v, double mass)
  290. {
  291.    double Ln, Lt;      // normal and tangential components of slip vector
  292.    double l;           // magnitude of slip vector, ft. per sec.
  293.    double F;           // force on car from track, lb.
  294.    double Fn, Ft;      // tangential and normal (to car's path) force components
  295.  
  296.    Ln = -vc * sine;   Lt = v - vc * cosine; // vector sum to compute slip vector
  297.    l = vec_mag(Lt, Ln);                     // compute slip speed
  298.    F = mass * g * friction(l);                 // compute friction force from track
  299.    if(l < .0001)              // to prevent possible division by zero
  300.       Fn = Ft = 0.0;
  301.    else  {
  302.       Fn = -F * Ln/l;      // compute components of force vector
  303.       Ft = -F * Lt/l;
  304.    }
  305.    // compute power excess over target value of .9975*PM:
  306.    return (vc < 0.0 ? -vc : vc) * (Ft * cosine + Fn * sine) - .9975*PM;
  307. }
  308.  
  309. inline double SIGN(double a, double b)   // something needed by zbrent()
  310. {
  311.    return b >= 0.0 ? fabs(a) : -fabs(a);
  312. }
  313.  
  314. // This routine was adapted from the book "Numerical Recipes in C" by
  315. // Press, et. al.  It searches for a value of vc which causes the
  316. // power to be very close to the maximum available.
  317. // (This is Brent's method of root finding.)  x1 and x2 are values of
  318. // vc which bracket the root.  b represents the variable vc.
  319. // (See power_excess(), above.)
  320. double zbrent(double sine, double cosine, double v, double x1, double x2, double mass, double tol)
  321. {
  322.    const int ITMAX = 20;
  323.    const double EPS = 1.0e-8;
  324.     int iter;
  325.     double a=x1, b=x2, c=x2, d,e,min1,min2;
  326.     double fa=power_excess(a, sine, cosine, v, mass);
  327.     double fb=power_excess(b, sine, cosine, v, mass);
  328.    double fc,p,q,r,s,tol1,xm;
  329.    double Ln, Lt;      // normal and tangential components of slip vector
  330.    double l;           // magnitude of slip vector, ft. per sec.
  331.    double F;           // force on car from track, lb.
  332.    double Fn, Ft;      // tangential and normal (to car's path) force components
  333.  
  334.     if ((fa > 0.0 && fb > 0.0) || (fa < 0.0 && fb < 0.0))
  335.         return b;              // This should never happen.
  336.     fc=fb;
  337.     for (iter=1;iter<=ITMAX;iter++) {
  338.         if ((fb > 0.0 && fc > 0.0) || (fb < 0.0 && fc < 0.0)) {
  339.             c=a;
  340.             fc=fa;
  341.             e=d=b-a;
  342.         }
  343.         if (fabs(fc) < fabs(fb)) {
  344.             a=b;
  345.             b=c;
  346.             c=a;
  347.             fa=fb;
  348.             fb=fc;
  349.             fc=fa;
  350.         }
  351.         tol1=2.0*EPS*fabs(b)+0.5*tol;
  352.         xm=0.5*(c-b);
  353.         if (fabs(xm) <= tol1 || fb == 0.0)
  354.          return b;
  355.         if (fabs(e) >= tol1 && fabs(fa) > fabs(fb)) {
  356.             s=fb/fa;
  357.             if (a == c) {
  358.                 p=2.0*xm*s;
  359.                 q=1.0-s;
  360.             } else {
  361.                 q=fa/fc;
  362.                 r=fb/fc;
  363.                 p=s*(2.0*xm*q*(q-r)-(b-a)*(r-1.0));
  364.                 q=(q-1.0)*(r-1.0)*(s-1.0);
  365.             }
  366.             if (p > 0.0) q = -q;
  367.             p=fabs(p);
  368.             min1=3.0*xm*q-fabs(tol1*q);
  369.             min2=fabs(e*q);
  370.             if (2.0*p < (min1 < min2 ? min1 : min2)) {
  371.                 e=d;
  372.                 d=p/q;
  373.             } else {
  374.                 d=xm;
  375.                 e=d;
  376.             }
  377.         } else {
  378.             d=xm;
  379.             e=d;
  380.         }
  381.         a=b;
  382.         fa=fb;
  383.         if (fabs(d) > tol1)
  384.             b += d;
  385.         else
  386.             b += SIGN(tol1,xm);
  387.       Ln = -b * sine;   Lt = v - b * cosine; // vector sum to compute slip vector
  388.       l = vec_mag(Lt, Ln);                     // compute slip speed
  389.       F = mass * g * friction(l);                 // compute friction force from track
  390.       if(l < .0001)              // to prevent possible division by zero
  391.          Fn = Ft = 0.0;
  392.       else  {
  393.          Fn = -F * Ln/l;      // compute components of force vector
  394.          Ft = -F * Lt/l;
  395.       }
  396.       // compute power delivered:
  397.       fb = (b < 0.0 ? -b : b) * (Ft * cosine + Fn * sine) - .9975*PM;
  398.     }
  399.     return b;
  400. }
  401.  
  402. // Simulates the physics, moves the car by changing the state variables.
  403. // The angle "alpha" is the angle between the car's orientation angle and
  404. // its velocity vector.  (like angle of attack of an aircraft)
  405. // Cornering force depends on alpha.  (It is also thought of as a slip angle.)
  406. // "Slip" refers to wheel vs. track motion.
  407. // "vc" is the speed of the bottom of the wheel relative to the car.
  408. // This model is like a four-wheel drive car, since the forces are not
  409. // computed separately for front and rear wheels.
  410. void Car::move_car()
  411. {
  412.    double D;        // force on car from air, lb.
  413.    double Fn, Ft;      // normal & tangential components of track force vector
  414.    double P;           // power delivered to track, ft. lb. per sec.
  415.    double v;           // car's speed
  416.    double Ln, Lt;      // normal and tangential components of slip vector
  417.    double l;           // magnitude of slip vector, ft. per sec.
  418.    double F;           // force on car from track, lb.
  419.    double x_a, y_a;    // accelleration components in x & y directions
  420.    double sine, cosine, separation, dx, dy;
  421.    double pvec_x, pvec_y;   // pointing vector of car (velocity vec + alpha)
  422.    int i, other_seg;
  423.    double dot, mag_prod, temp;
  424.    double rel_xdot, rel_ydot;   // velocity relative to other car being hit
  425.    double mass;                 // current mass of car
  426.  
  427.    v = vec_mag(xdot, ydot);                     // the car's speed, feet/sec
  428.    if(v > speed_max)                            // keep track of max speed
  429.         speed_max = v;
  430.    mass = M + fuel/g;                             // current mass of car + fuel
  431.    // limit how fast alpha can change, and its maximum value
  432.    alpha = alpha_limit(prev_alpha, alpha);
  433.    prev_alpha = alpha;
  434.    sine = sin(alpha);   cosine = cos(alpha);    // alpha is angle of attack
  435.    // don't allow reverse gear
  436.    if(vc < 0.0)    // This would be a reverse gear request
  437.       if(v > REVERSE_GEAR_LIMIT)   // if going too fast
  438.          vc = 0.0;                      // make it maximum braking
  439.  
  440.    // check accumulated damage and fuel, take car out of race if necessary:
  441.    if((stage != PRACTICE && damage > MAX_DAMAGE) || fuel <= 0.0) {
  442.          if(offroad) {
  443.             if(damage > MAX_DAMAGE)  {
  444.                if(!out)
  445.                   ++out_count;    // Put it out of race if too much damage.
  446.                xdot = ydot = 0.0;   // stop it dead.
  447.                out = 1;
  448.                return;
  449.             }
  450.             if(full_time == 0.0) {    // sign that car just ran out of gas
  451.                out = 1;
  452.                xdot = ydot = 0.0;
  453.                full_time = time_count + REFILL_SECONDS;
  454.             }
  455.             else if(full_time < time_count) {
  456.                out = 0;
  457.                full_time = 0.0;
  458.                fuel = FUEL_START;
  459.             }
  460.          }
  461.          else {
  462.             sine = alpha = -.005;   // veer off to the right
  463.             cosine = .99999;
  464.             vc = 88.0;       // approach 60 mph.
  465.          }
  466.       }
  467.    if(out)            // This gets set if the car is stuck and off the track
  468.       return;
  469.  
  470.    dead_ahead = 0;   // will be set if there is a car more-or-less dead ahead
  471.    for(i=0; i<car_count; i++) {     // check for cars nearby or bumping
  472.       if(i == which) continue;         // ignore oneself
  473.       other_seg = pcar[i]->seg_id;     // only consider present and next segment
  474.       if(!(seg_id == other_seg || incseg(seg_id) == other_seg))
  475.           continue;
  476.       dx = pcar[i]->x - x;      // components of vector to other car
  477.       dy = pcar[i]->y - y;
  478.       separation = vec_mag(dx, dy);      // distance to the other car
  479.       if(separation > 2.5 * CARLEN)      // ignore cars farther away than this
  480.          continue;
  481.       // Is there a car dead ahead?  "dead ahead" means within 2.5 car lengths
  482.       // AND within a few degrees of pointing vector  (arcos(.94) == 20 deg.)
  483.       // First compute pointing vector by rotating velocity vector by alpha:
  484.       pvec_x = xdot * cosine - ydot * sine;  // components of pointing vector:
  485.       pvec_y = xdot * sine + ydot * cosine;
  486.       // compute dot product, rel. position & pointing vectors:
  487.       dot = pvec_x * dx + pvec_y * dy;
  488.       mag_prod = separation * v;    // p_vec and v vectors are same length
  489.       if(dot > .94 * mag_prod)      // test based on properties of dot product
  490.          dead_ahead = 1;            // there is a car more-or-less dead ahead
  491.       if(separation > CARLEN)    // If separation is greater than length,
  492.            continue;             // cars cannot be bumping.
  493.       // cars might be bumping, test for that.  Simplified test, assumes
  494.       // cars are parallel.  Also, only test for a car in front, as the
  495.       // other car will test also, and find this car in front.
  496.       if(v < .01)              // to prevent division error just below
  497.          continue;
  498.       temp = dot / v;          // dot/v is longitudinal component of separation
  499.       if(temp > CARLEN || dot < 0.0)
  500.          continue;
  501.       if(separation - temp/separation > CARWID)  // test for lateral separation
  502.          continue;
  503.       // The cars are bumping, reduce speed of rear car, and damage both;
  504.       // The damage is proportional the the square of the relative velocity.
  505.       rel_xdot = xdot - pcar[i]->xdot;
  506.       rel_ydot = ydot - pcar[i]->ydot;
  507.       temp = rel_xdot * rel_xdot + rel_ydot * rel_ydot;  //impact energy
  508.       damage += .62 * temp;
  509.       pcar[i]->damage += .35 * temp;   // less damage to car in front
  510.       xdot *= .8;
  511.       ydot *= .8;
  512.    }
  513.  
  514.    int it = 0;  // This is a loop counter.
  515.    VC:    // maybe loop to control power (we don't permit P > PM)
  516.  
  517.    Ln = -vc * sine;   Lt = v - vc * cosine; // vector sum to compute slip vector
  518.    l = vec_mag(Lt, Ln);                     // compute slip speed
  519.    F = mass * g * friction(l);          // compute friction force from track
  520.    D = DRAG_CON * v * v;                    // air drag force
  521.    if(offroad)  {                       // if the car is off the track,
  522.       D = (0.6 + .008 * v) * mass * g;     // add a lot more resistance
  523.       if(veryoffroad)
  524.          D += 1.7 * mass * g;
  525.    }
  526.    if(l < .0001)              // to prevent possible division by zero
  527.       Fn = Ft = 0.0;
  528.    else  {
  529.       Fn = -F * Ln/l;      // compute components of force vector
  530.       Ft = -F * Lt/l;
  531.    }
  532.    // compute power delivered:
  533.    P =  (vc < 0.0 ? -vc : vc) * (Ft * cosine + Fn * sine);
  534.  
  535.    if(!it)  {         // If this is the first time through here, then:
  536.       power_req = P/PM;  // Tell the driver how much power it requested.
  537.       if(P > PM) {       // If the request was too high, reduce it to 100% pwr.
  538.           ++it;
  539.           vc = zbrent(sine, cosine, v, v * cosine, vc, mass, .006);
  540.           goto VC;
  541.       }
  542.    }
  543.    power = P/PM;   // store this value in the car object
  544.  
  545.    // put some randomness in the magnitude of the traction force, F:
  546.    // (Ft might be set to 0.0 above, in which case F will be zero.)
  547.    if(Ft != 0.0 && randomotion)
  548.       temp = rfriction(l) * mass * g / F;  // ratio of new to original force
  549.    else
  550.       temp = 1.0;
  551.  
  552.    // compute centripetal and tangential acceleration components:
  553.    cen_a = Fn * temp /  mass;
  554.    tan_a = (Ft * temp - D) / mass;
  555.  
  556.    if(P > 0.0)
  557.       fuel -= P * SFC * delta_time;
  558.  
  559.    if(offroad)  {    // If off the track the car accumulates damage.
  560.       // damage is proportional to square of acceleration:
  561.       damage += int(tan_a * tan_a + cen_a * cen_a) / 10;
  562.    }
  563.  
  564.    if(v < .0001)                         // prevent division by zero
  565.       adot = sine = cosine = 0.0;
  566.    else  {
  567.      adot = cen_a / v;                         // angular velocity
  568.      sine = ydot/v;   cosine = xdot/v;         // direction of motion
  569.    }
  570.    x_a = tan_a * cosine - cen_a * sine;      // x & y components of acceleration
  571.    y_a = cen_a * cosine + tan_a * sine;
  572.  
  573.    // Advance the state using the Adam's predictor formula:
  574.    x += (1.5 * xdot - .5 * pre_xdot) * delta_time;
  575.    y += (1.5 * ydot - .5 * pre_ydot) * delta_time;
  576.    pre_xdot = xdot;  pre_ydot = ydot;
  577.    xdot += (1.5 * x_a - .5 * pre_x_a) * delta_time;
  578.    ydot += (1.5 * y_a - .5 * pre_y_a) * delta_time;
  579.    pre_x_a = x_a;   pre_y_a = y_a;
  580.    if(v >= .0001)
  581.       ang = atan2(ydot,xdot);                   // new orientation angle
  582. }
  583.  
  584.  
  585. void Car::draw_car(void)  // Calls drawcar() twice, once to erase, once to draw
  586. {
  587.    if(out)       // Don't redraw a car that is out of the race.
  588.       return;
  589.    drawcar(prex, prey, prang, TRACK_COLOR, TRACK_COLOR);     // erase old one
  590.    drawcar(x, y, prang = ang+alpha, nose_color, tail_color); // draw new one
  591.    prex = x;   prey = y;     // save these to erase car next time
  592. }
  593.  
  594. // The Car constructor:
  595. Car::Car(int i)
  596. {
  597.    which = i;                    // Each car has it own index into pcar[]
  598.    cntrl = drivers[i].rob_ptr;      // Each car has a pointer to its driver.
  599.    nose_color = drivers[i].paint_job.nose;
  600.    tail_color = drivers[i].paint_job.tail;
  601.    prex = trackout[0].beg_x;            // These 3 are initialized so that
  602.    prey = trackout[0].beg_y + width/2;  // draw_car() will work OK the
  603.    prang = 0.0;                         // first time it is called.
  604.    power_req = .9;      // meaningless, but will be soon replaced by move_car()
  605.    data_ptr = (void*)new char[PRIV_DATA_SIZE]; // give the robot some RAM to use
  606. }
  607.  
  608. void Car::put_car(double x_pos, double y_pos, double alf_ang)
  609. {
  610.    x = x_pos;
  611.    y = y_pos;
  612.    ang = alf_ang;
  613.    to_end = trackin[0].length;   // not exact, but corrected by observe()
  614.    alpha = ydot = ang = prev_alpha = full_time = 0.0;
  615.    lap_time = speed_avg = speed_max = 0;
  616.    laps = -1;    // to become 0 crossing the finish line at start of race
  617.    starting = 1;
  618.    out = lap_flag = seg_id = dead_ahead = 0;
  619.    init_flag = 0;
  620.    damage = 0;
  621.    xdot = vc = STARTING_SPEED;
  622.    fuel = FUEL_START;
  623.    pre_x_a = pre_y_a = pre_xdot = pre_ydot = 0.0;
  624.    if(!no_display)
  625.       draw_car();    // draw the new car on the screen
  626. }
  627.  
  628. // This function uses the current state of the car, and the current
  629. // track segment data, to compute the car's local situation as seen by
  630. // the driver.  (see struct situation)
  631. void Car::observe(rel_state* rel_vec_ptr, situation& s)
  632. {
  633.    double rad;                // current radius
  634.    double dx, dy, xp, yp;
  635.    double sine, cosine, dot;  // dot used for dot product of two vectors
  636.    double temp;
  637.    int nex_seg;         // segment ID of the next segment
  638.    double separation, dxdot, dydot;
  639.    int i, k, m, other_seg;
  640.    // These two arrays describe the three closest cars:
  641.    // element 0 is for the closest, element 1 next, element two after that.
  642.    int closest[] = { 999, 999, 999 };       // their ID's, initially too big
  643.    double how_close[] = { 1e5, 1e5, 1e5 };  // initially very large distances
  644.    int flag = 1;        // controls possible repetition of calculations due to
  645.                         // completion of a lap.
  646.    if(out)            // This gets set if the car is stuck and off the track
  647.       return;
  648.  
  649.    s.nearby = rel_vec_ptr;            // the robot's pointer to his RAM area
  650.    s.v      = vec_mag(xdot, ydot);    // the actual speed
  651.    s.dead_ahead = dead_ahead;         // copy the value set by move_car()
  652.    s.power_req = power_req;           // copy the value set by move_car()
  653.    s.power  = power;                  // copy the value set by move_car()
  654.    s.fuel   = fuel;                   // copy the value set by move_car()
  655.    s.damage = damage;                 // copy the value set by move_car()
  656.    s.cen_a  = cen_a;                  // copy the value set by move_car()
  657.    s.tan_a  = tan_a;                  // copy the value set by move_car()
  658.    s.alpha  = alpha;                  // copy the value set by move_car()
  659.    s.vc     = vc;
  660.    s.start_time = start_time;
  661.    s.lap_flag = lap_flag;
  662.    s.time_count = time_count;         // copy the global value
  663.    s.stage = stage;                   // copy the global value
  664.    s.position = position[which];
  665.    s.my_ID = which;
  666.  
  667.    // Computations below are based on the data in trackin[] and
  668.    // trackout[].  Radii are based on the inside rail in each case.
  669.  
  670.    while(flag)  {  // This loop repeats only when a segment boundary is crossed,
  671.                    //   in which case it repeats once:
  672.  
  673.    // compute sine and cosine of track direction:
  674.    sine = sin(temp = trackout[seg_id].beg_ang);
  675.    cosine = cos(temp);
  676.  
  677.    if((nex_seg = seg_id + 1) == NSEG)               // which segment is next
  678.       nex_seg = 0;
  679.    s.nex_len = trackout[nex_seg].length;            // length and radius
  680.    s.nex_rad = trackin[nex_seg].radius;             // of next segment
  681.    if(s.nex_rad < 0.0)                              // always use smaller radius
  682.       s.nex_rad = trackout[nex_seg].radius;
  683.  
  684.    if(++nex_seg == NSEG)
  685.       nex_seg = 0;
  686.    s.after_len = trackout[nex_seg].length;            // length and radius
  687.    s.after_rad = trackin[nex_seg].radius;         // and the one after that
  688.    if(s.after_rad < 0.0)                          // always use smaller radius
  689.       s.after_rad = trackout[nex_seg].radius;
  690.  
  691.    if(++nex_seg == NSEG)
  692.       nex_seg = 0;
  693.    s.aftaft_len = trackout[nex_seg].length;            // length and radius
  694.    s.aftaft_rad = trackin[nex_seg].radius;         // and the one after that
  695.    if(s.aftaft_rad < 0.0)                          // always use smaller radius
  696.       s.aftaft_rad = trackout[nex_seg].radius;
  697.  
  698.    s.cur_len = trackout[seg_id].length;           // copy these two fields:
  699.    s.cur_rad = rad = trackin[seg_id].radius;  // rt. turn will use trackout
  700.    if(seg_id != 0)      // This is used to tell when the finish line is 
  701.       lap_flag = 0;     // crossed, thus completing each lap.     
  702.  
  703.    if(rad == 0) {   // if current segment is straight,
  704.       // xp and yp locate the car with respect to the beginning of the right
  705.       // hand wall of the straight segment.  calculate them:
  706.       dx = x - trackout[seg_id].beg_x;
  707.       dy = y - trackout[seg_id].beg_y;
  708.       xp = dx * cosine + dy * sine;
  709.       yp = dy * cosine - dx * sine;
  710.       s.to_rgt = yp;                   // fill in to_rgt and to_end:
  711.       s.to_end = s.cur_len - xp;
  712.  
  713.       if(seg_id == 0)
  714.          if(xp > FINISH * s.cur_len && !lap_flag) {
  715.             lap_flag = 1;        // This means the line crossing was noted.
  716.             if(++laps == (stage == PRACTICE ? practice : lap_count))
  717.                ++done_count;
  718.             if(!no_display)
  719.                lapper(which, laps);  // display the new lap count
  720.             lap[which] = 1;
  721.             if(laps == 0)       // record when the starting line is crossed:
  722.                 start_time = last_crossing = time_count;
  723.             else      // update overall average speed:
  724.                 speed_avg = length * laps / (time_count-start_time);
  725.                 lap_time = time_count - last_crossing;
  726.                 last_crossing = time_count;
  727.          }
  728.  
  729.       // here we make sure we are still in the same segment:
  730.       if(s.to_end <= 0.0)  {           // see if a lap has been completed,
  731.          if(++seg_id == NSEG)
  732.             seg_id = 0;
  733.          continue;           // repeat the loop in context of next segment
  734.       }
  735.       s.to_lft = width - yp;           // fill in to_lft & cur_rad:
  736.       s.cur_rad = 0.0;
  737.       s.vn      = ydot * cosine - xdot * sine;  // compute cross-track speed
  738.       s.backward = (xdot * cosine + ydot * sine < 0.0);
  739.    }
  740.    else if(rad > 0.0) {      // when current segment is a left turn:
  741.       dx = x - trackout[seg_id].cen_x; // compute position relative to center
  742.       dy = y - trackout[seg_id].cen_y;
  743.       temp = atan2(dy, dx);           // this is the current angular position
  744.       s.to_end = trackout[seg_id].end_ang - temp - PI/2.0;  // this is an angle
  745.       if(s.to_end > 1.5 * PI)
  746.          s.to_end -= 2.0 * PI;
  747.       else if(s.to_end < -.5 * PI)
  748.          s.to_end += 2.0 * PI;
  749.       if(s.to_end <= 0.0)  {           // Handle segment crossing:
  750.          if(++seg_id == NSEG)
  751.             seg_id = 0;                // going from last segment to 1st
  752.          continue;
  753.       }
  754.       s.to_lft = vec_mag(dx, dy) - rad;
  755.       s.to_rgt = width - s.to_lft;
  756.       s.vn = (-xdot * dx - ydot * dy)/vec_mag(dx, dy);   // a trig thing
  757.       s.backward = (ydot * dx - xdot * dy < 0.0);
  758.    }
  759.    else {
  760.       s.cur_rad = rad = trackout[seg_id].radius;  // rt. turn needs trackout
  761.       dx = x - trackout[seg_id].cen_x; // compute position relative to center
  762.       dy = y - trackout[seg_id].cen_y;
  763.       temp = atan2(dy, dx);           // this is the current angular position
  764.       s.to_end = -trackout[seg_id].end_ang + temp - PI/2.0;  // this is an angle
  765.       if(s.to_end < -.5 * PI)
  766.          s.to_end += 2.0 * PI;
  767.       else if(s.to_end >= 1.5 * PI)
  768.          s.to_end -= 2.0 * PI;
  769.       if(s.to_end <= 0.0)  {           // Handle segment transistion:
  770.          if(++seg_id == NSEG)
  771.             seg_id = 0;
  772.          continue;
  773.       }
  774.       s.to_rgt = vec_mag(dx, dy) + rad;
  775.       s.to_lft = width - s.to_rgt;
  776.       s.vn = (xdot * dx + ydot * dy)/vec_mag(dx, dy);   // a trig thing
  777.       s.backward = (xdot * dy - ydot * dx < 0.0);
  778.    }
  779.  
  780.    // If we get this far, we do not repeat the big loop.
  781.    flag = 0;
  782.    }  // end while(flag) loop
  783.  
  784.    s.seg_ID = seg_id;
  785.    s.lap_time = lap_time;
  786.    s.laps_to_go = lap_count - laps;
  787.    if(s.laps_to_go > lap_count)
  788.       s.laps_to_go = lap_count;
  789.    offroad = (s.to_lft < 0.0 || s.to_rgt < 0.0);   // maybe set offroad flag
  790.    veryoffroad = (s.to_lft < -width || s.to_rgt < -width); // maybe veryoffroad
  791.    to_end = s.to_end;                       // fill in this field in Car object
  792.  
  793.    if(s.backward)
  794.       return;         // If going backwards, skip looking for nearby cars.
  795.  
  796.    // find three closest cars in front:
  797.    // "front", here, is direction of velocity vector, not pointing vector.
  798.    for(i=0; i<car_count; i++) {     // check for cars nearby
  799.       if(i == which) continue;         // ignore oneself
  800.       other_seg = pcar[i]->seg_id;     // only consider present and next segment
  801.       if(!(seg_id == other_seg || incseg(seg_id) == other_seg))
  802.           continue;
  803.       dx = pcar[i]->x - x;      // components of vector to other car
  804.       dy = pcar[i]->y - y;
  805.       separation = vec_mag(dx, dy);      // distance to the other car
  806.       // compute dot product, rel. position & velocity vectors:
  807.       dot = xdot * dx + ydot * dy;
  808.       if(dot < 0.0)
  809.          continue;              // Ignore cars behind you.
  810.       for(k=0; k<3; k++)  {
  811.          if(separation < how_close[k])  {
  812.             for(m = 2; m > k; m--) {
  813.                closest[m] = closest[m-1];
  814.                how_close[m] = how_close[m-1];
  815.             }
  816.             closest[k] = i;
  817.             how_close[k] = separation;
  818.             break;
  819.          }
  820.       }
  821.    }
  822.      // now compute local relative state vector for those three cars:
  823.    for(k=0; k<3; k++)  {
  824.       if(closest[k] > car_count) {  // This is when there are less than 3
  825.          for(; k<3; k++)
  826.             s.nearby[k].who = 999;
  827.          break;
  828.       }
  829.       i = closest[k];
  830.       dx = pcar[i]->x - x;      // components of relative position vector
  831.       dy = pcar[i]->y - y;
  832.       dxdot = pcar[i]->xdot - xdot; // components of relative velocity
  833.       dydot = pcar[i]->ydot - ydot;
  834.       // The relative vectors must be found wrt to velocity vector frame.
  835.       // we need sine and cosine of rotation of axes:
  836.       if(s.v > 1e-7)  {        // prevent division by 0
  837.          sine = - xdot / s.v;                 // direction of velocity
  838.          cosine = ydot / s.v;                 // wrt the x axis.
  839.       }
  840.       else  {
  841.          sine = -1.0;
  842.          cosine = 0.0;
  843.       }
  844.       s.nearby[k].who = i;
  845.       s.nearby[k].rel_x = cosine * dx + sine * dy;
  846.       s.nearby[k].rel_y = cosine * dy - sine * dx;
  847.       s.nearby[k].rel_xdot = cosine * dxdot + sine * dydot;
  848.       s.nearby[k].rel_ydot = cosine * dydot - sine * dxdot;
  849.    }
  850. }
  851.  
  852. // Get the names from the robot drivers and store them in RAM.
  853. // Fill in drivers[i].rob_name, which is an array of pointers to the names.
  854. // some of these pointers may point to the same place; others may be 0;
  855. void get_names(void)
  856. {
  857.    int i, j;
  858.    situation s;                    // to pass data from observe() to control()
  859.    rel_state rel_state_vec[3];     // needed by fill_situation() function
  860.  
  861.    for(i=0; i<MAXCARS; i++) {
  862.       glob_name[0] = 0;        // to check later to see if driver filled it
  863.       // fill in s to avoid div. by zero in driver.
  864.       s = fill_situation(rel_state_vec);
  865.       // The first call to a robot driver may put its name in glob_name.
  866.       (void)drivers[i].rob_ptr(s);     // Call the robot "driver" function.
  867.       if(glob_name[0] != 0)  {   // add new name to drivers[] array:
  868.          drivers[i].rob_name = new char[strlen(glob_name) + 1];
  869.          strcpy(drivers[i].rob_name, glob_name);
  870.          if(strlen(glob_name) > 9)     // chop any names greater than 8 chars.
  871.             *(drivers[i].rob_name + 8) = '\0'; 
  872.       }
  873.       else                       // if no name was given, see if there is
  874.          for(j=i-1; j>=0; j--)   // already a name for that driver:
  875.             if(drivers[i].rob_ptr == drivers[j].rob_ptr) {
  876.                drivers[i].rob_name = drivers[j].rob_name;
  877.                break;
  878.             }
  879.    }
  880. }
  881.  
  882. static int racers[MAXCARS];    // the starting arrangement goes here
  883.  
  884. // fills racers{} with random permutation of 0, 1, 2..., car_count-1
  885. void pick_random_order(void)
  886. {
  887.      int selected[MAXCARS];  // marks the points as they are picked
  888.      int i, j, k, count;
  889.  
  890.      for(i=0; i<car_count; i++) {     // initialize selected[] to all zeroes:
  891.          selected[i] = 0;    // (meaning no points are picked)
  892.      }
  893.  
  894.      // for each element of rptr, j is no. of alternates
  895.      for(j=car_count; j>0; j--) {  // remaining.  here we pick one of them:
  896.         k = j>1 ? (raand()/128)%j : 0;       // k chooses among the remaining pts.
  897.          for(i=0, count=0; i<car_count; i++)
  898.             if(!selected[i])            // if not already picked,
  899.                if(count++ == k)  {
  900.                    selected[i] = 1;     // marks this point as taken
  901.                    racers[j-1] = i;       // puts it into the target
  902.                }
  903.      }
  904. }
  905.  
  906. // Reverse the starting order of the cars, by reversing the racers[] array:
  907. void reverse_order(void)
  908. {
  909.    int temp;
  910.  
  911.    for(int i=0; i<car_count/2; i++) {
  912.         temp = racers[i];
  913.         racers[i] = racers[car_count - 1 - i];
  914.         racers[car_count - 1 - i] = temp;
  915.    }
  916. }
  917.  
  918. main(int argc, char* argv[])   // args are interpreted by get_args() in OS.CPP
  919. {
  920.    int i, j, ml;
  921.    double x, y, dx, dy;            // used only for initial positions of cars
  922.    double ip_update_time;          // ip is for instument panel
  923.    situation s;                    // to pass data from observe() to control()
  924.    int num_disp;   // how many cars to display in leaders area of scoreboard
  925.    int starting;        // indicates the race is just starting
  926.    int c;               // for a keyboard character code
  927.    rel_state rel_state_vec[3];    // contains up to 3 relative state vectors
  928.  
  929.    glob_name = new char[33];      // temporary storage for a driver's name
  930.    strcpy(glob_name, "Not yet filled in, 32 characters");
  931.  
  932.    get_names();  // call all the robot drivers to fill in the drivers[] array.
  933.  
  934.    // Set the track, car_count, lap_count, and various options:
  935.    get_args(argc, argv);
  936.    if(no_display)
  937.       real_speed = 0;
  938.  
  939.    // decide how many leaders to display on leader board:
  940.    num_disp = (car_count < 5) ? car_count : 5;  // show up to 5
  941.  
  942.    // setup the graphics display, also compute track data:
  943.    graph_setup();
  944.  
  945.    // get track description into this file's variables
  946.    NSEG = get_track_description().NSEG;
  947.    width = get_track_description().width;
  948.    trackin = get_track_description().trackin;
  949.    trackout = get_track_description().trackout;
  950.  
  951.    randomizer();   // maybe randomly initilize the random variable generator
  952.    report_overall(car_count, lap_count, drivers, Seed_to_report);
  953.  
  954.    for(i=0; i<car_count; i++) {
  955.       pcar[i] = new Car(i);    // create the car objects
  956.    }
  957.  
  958.    // Main loop - Once through here for every complete race or practice session
  959.  for(ml=0; ml<race_count; ++ml)  {
  960.    done_count = 0;  // incremented by each car that finishes the race
  961.    out_count = 0;   // incremented by each car that crashes
  962.    time_count = ip_update_time = 0.0;  // elapsed time, seconds
  963.    starting = 1;
  964.  
  965.    if(keep_order)          // decide on the starting positions
  966.       for(i=0; i<car_count; i++)
  967.          racers[i] = i;
  968.    else
  969.       if(!(ml%2))
  970.          pick_random_order();  // arrange the starting positions
  971.       else
  972.          reverse_order();      // reverse positions for 2nd race
  973.  
  974.    // arrange the cars on the track:
  975.    dy = width/4;          // the dy and dx stuff is for arranging
  976.    dx = 2.5 * CARLEN;     // the cars in their starting positions
  977.    x = trackout[0].beg_x - dx;                // We assume that segment 0
  978.    for(i=0; i<car_count; i++) {               // is straight, and
  979.       if(!(i%4)) {                            // parallel to the x axis.
  980.          y = trackout[0].beg_y + dy/2;        // Cars start on seg. 0.
  981.          x += dx;
  982.       }
  983.       else
  984.          y += dy;
  985.       pcar[racers[i]]->put_car(x, y, 0);   // This is the key part.
  986.    }
  987.  
  988.    // draw track boundaries:
  989.    if(ml && !no_display)  {
  990.       set_color(RAIL_COLOR);
  991.       (void)drawpath(TRK_STRT_X, TRK_STRT_Y, 0, trackout);
  992.       (void)drawpath(TRK_STRT_X, TRK_STRT_Y+width, 0, trackin);
  993.    }
  994.  
  995.    if(!car_count) {  //When zero cars are requested, we only show the track.
  996.       if(no_display)  {
  997.          cout << "Zero cars were requested." << endl;
  998.          exit(0);
  999.       }
  1000.       get_ch();         // this executes only when 0 cars are requested
  1001.       resume_normal_display();
  1002.       exit(0);
  1003.    }
  1004.  
  1005.    if(practice && stage == BEFORE) {
  1006.       stage = PRACTICE;
  1007.       --ml;
  1008.    }
  1009.    else
  1010.       stage = RACING;
  1011.  
  1012.    // Put up the scoreboard and leader board:
  1013.    if(!no_display)
  1014.       scoreboard(stage);
  1015.  
  1016.    done_count = 0;
  1017.    one_tick(1);
  1018.    order = new int[car_count];    //setup the order[] array:
  1019.    position = new int[car_count];    //setup the position[] array:
  1020.    new_data = new int[num_disp];  //setup the new_data[] array:
  1021.    for(i=0, j=car_count-1; i<car_count; i++, j--) { // initialize order[] array
  1022.       order[i] = j;            // must correspond with initial track positions
  1023.       position[j] = i;
  1024.       lap[i] = 1;
  1025.    }
  1026.    if(!no_display)
  1027.       // Don't actually start the race until someone hits a key.
  1028.       if(get_ch() == ESC)  // Any key except ESC begins the race.
  1029.          break;
  1030.    // BEGIN THE RACE!
  1031.    // Note that the lap count on the scoreboard is updated by the
  1032.    // observe() function, which call lapper() to do that.
  1033.    if(no_display)
  1034.       cout << "Beginning race " << (ml+1) << endl;
  1035.    else   {
  1036.       designated = -1;        // the instrument panel is initially inactive
  1037.       designate(designated);
  1038.    }
  1039.  
  1040.    while(1) {                             // main loop of the race:
  1041.       for(i=0; i<car_count; i++) {            // for each car:
  1042.          pcar[i]->observe(rel_state_vec, s); // compute its local situation
  1043.          pcar[i]->control(s);                 // compute a control vector
  1044.       }
  1045.       for(i=0; i<car_count; i++) {        // for each car:
  1046.          pcar[i]->move_car();                 // update state of car
  1047.       }
  1048.       if(!no_display)
  1049.          for(i=0; i<car_count; i++)       // for each car:
  1050.            pcar[i]->draw_car();               // update screen image of car
  1051.       time_count += delta_time;           // Advance the simulated time.
  1052.       if(!no_display && designated >= 0)  // maybe update the instrument panel
  1053.          if(time_count - ip_update_time > .25) {   // every .25 seconds
  1054.             instruments(designated);
  1055.             ip_update_time = time_count;
  1056.          }
  1057.  
  1058.       // This section is for the leader board:
  1059.       sortem(num_disp);      // maintains the order[] and new_data[] arrays
  1060.       // For any line flagged by new_data[], update that line in "LEADERS" area:
  1061.       int pos_done = 0;
  1062.       for(i=0; i<num_disp; i++)
  1063.          if(starting || new_data[i] || lap[order[i]]) {
  1064.             lap[order[i]] = 0;
  1065.             if(!no_display)
  1066.                leaders(i, order);
  1067.             if(!pos_done)  {          // compute position[] only when needed:
  1068.                for(int kk = 0; kk < car_count; kk++)
  1069.                   position[order[kk]] = kk;
  1070.                pos_done = 1;
  1071.             }
  1072.          }
  1073.  
  1074.       if(do_kb())   // check the keyboard rarely if trying to go fast
  1075.          if(kb_hit())    // respond to the keyboard if its hit
  1076.             if((c = get_ch()) == ESC)     // ESC key will end the race.
  1077.                break;
  1078.             else if(!no_display)          // F and S keys will speed up/slow down.
  1079.                if(c == UP)  {             // up and down arrow designate next car.
  1080.                   if(--designated < 0)  {
  1081.                      designated = - 1;
  1082.                      instruments(designated);
  1083.                   }
  1084.                   designate(designated);
  1085.                }
  1086.                else if(c == DOWN)  {
  1087.                   if(++designated >= car_count)
  1088.                      designated = car_count - 1;
  1089.                   designate(designated);
  1090.                }
  1091.                else if(real_speed && (c == 'f' || c == 'F'))
  1092.                   real_speed = 0;
  1093.                else if(!real_speed && (c == 's' || c == 'S'))
  1094.                   real_speed = 1;
  1095.                else if(c == 'p' || c == 'P')      // P is the Pause key
  1096.                   get_ch();
  1097.              
  1098.       if(!no_display)
  1099.          refresh_finish_line();
  1100.       // check for race over:
  1101.       if(done_count >= 2 || done_count >= car_count - out_count)
  1102.          break;
  1103.       // This is where (maybe) we slow the race down to realistic speed:
  1104.       if(real_speed)
  1105.          one_tick(0);     // wait here till the next clock tick
  1106.       starting = 0;       // the race has begun!
  1107.    }
  1108.    if(no_display)
  1109.       cout << "End of race " << (ml+1) << endl;
  1110.  
  1111.    if(stage == RACING)
  1112.       report_results(ml+1, order, drivers, pcar, racers);
  1113.  
  1114.    stage = RACING;
  1115.  
  1116.    if(!no_display)
  1117.       if(get_ch() == ESC)  // Continue showing end of race until a key is hit.
  1118.           break;
  1119.  
  1120.  } // End of Main Loop.
  1121.  
  1122.    /* clean up, end graphics, back to normal */
  1123.    resume_normal_display();
  1124.    RAM_report();
  1125.    return 0;  // all done with execution, return to DOS.
  1126. }
  1127.  
  1128.